package com.fdb.efp.nls.service.impl.sx;

import com.fdb.efp.nls.service.facade.NlsApplyInfoService;
import com.fdb.efp.nls.service.facade.NlsQueueSoltComnService;
import com.fdb.efp.nls.service.facade.queue.NlsProcessBizService;
import com.fdb.efp.nls.service.facade.queue.NlsProcessRuleService;
import com.fdb.efp.nls.service.facade.sx.NlsCreditInfoService;
import com.fdb.efp.nls.service.vo.NlsApplyInfoVO;
import com.fdb.efp.nls.service.vo.queue.NlsProcessBizVO;
import com.fdb.efp.nls.service.vo.queue.NlsProcessRuleVO;
import com.fdb.efp.nls.service.vo.sx.NlsCreditInfoVO;
import com.fdb.basic.framework.core.constant.BaseConstant;
import com.fdb.basic.framework.core.exception.BizException;
import com.fdb.basic.framework.core.util.DateUtility;
import com.fdb.basic.framework.core.util.MD5Util;
import com.fdb.basic.framework.core.util.SpringContextUtils;
import com.fdb.basic.framework.core.util.ValidationUtils;
import com.fdb.efp.nls.common.ConsoleEnums;
import com.fdb.efp.nls.common.PrdStatEnums;
import com.fdb.efp.nls.service.facade.PrdInfoService;
import com.fdb.efp.nls.service.facade.PrdSoltRuleService;
import com.fdb.efp.nls.service.facade.SCfgParamService;
import com.fdb.efp.nls.service.vo.PrdInfoVO;
import com.fdb.efp.nls.service.vo.PrdSoltRuleVO;
import com.fdb.efp.console.service.vo.SCfgParamVO;
import com.fdb.efp.nls.service.facade.CtrLoanContService;
import com.fdb.efp.cus.service.facade.CusRelCheckService;
import com.fdb.efp.cus.service.facade.CusWhiteListTecService;
import com.fdb.efp.cus.service.vo.CusIndivRelVO;
import com.fdb.efp.cus.service.vo.CusRelCheckVO;
import com.fdb.efp.cus.service.vo.CusWhiteListTecVO;
import com.fdb.efp.esb.common.constant.MsLoanConstant;
import com.fdb.efp.esb.service.bo.req.NLMHitQueryReports;
import com.fdb.efp.esb.service.bo.req.ZX11001QueryReports;
import com.fdb.efp.esb.service.bo.resp.zx.NLMHitQueryResponseReports;
import com.fdb.efp.esb.service.facade.esb.EsbService;
import com.fdb.efp.esb.service.facade.zx.ZXService;
import com.fdb.efp.limit.service.facade.LmtPrdContService;
import com.fdb.efp.nls.service.facade.AccLoanService;
import com.fdb.efp.nls.common.constant.PrdAdaptedSoltEnums;
import com.fdb.efp.rule.common.enumeration.RefuseType;
import com.fdb.efp.rule.common.enumeration.Rule;
import com.fdb.efp.rule.service.facade.RuleCallService;
import com.fdb.efp.rule.service.vo.ReqAccessRuleVo;
import com.fdb.efp.rule.service.vo.ReqRuleVo;
import com.fdb.efp.rule.service.vo.RespRuleVO;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @description: 准入规则校验 一 根据申请信息与产品相关要素进行校验<br>
 *               1. 判断该客户当前申请的产品在我行是否存在授信有效期内的剩余可用额度<br>
 *               2. 根据当前客户查询是否已存借款（如马上），是否已经存在有效期内的剩余可用余额（如百度）<br>
 *               3. 根据当前客户查询客户限额，根据进件判断是否超过客户限额<br>
 *               二 产品校验<br>
 *               1. 通过申请流水号查询申请信息（nls_credit_info）<br>
 *				 2. 根据申请信息中的产品编号查询对应产品信息（prd_info）<br>
 *				 3. 将规则所需信息装入规则VO中<br>
 *               三 年龄判断<br>
 *               四 人群分层判断<br>
 *               五 行员关联人核查<br>
 *               六 黑名单核查<br>
 * @param appNo
 * @date:2019年3月5日 下午16:15:55
 * @author:heyingfeng
 */

@Service("creditAccessRuleService")
public class CreditAccessRuleServiceImpl extends CreditSoltServiceImpl {

	private static final Logger log = LoggerFactory.getLogger(CreditAccessRuleServiceImpl.class);
	
	@Override
	public void doExecute(NlsProcessBizVO nlsProcessBizVo, NlsCreditInfoVO nlsCreditInfoVO) throws Exception {
		String applySeq = nlsProcessBizVo.getApplySeq();
		String slotCode = nlsProcessBizVo.getSoltCode();
		String certType = nlsCreditInfoVO.getCertType();
		String certCode = nlsCreditInfoVO.getCertCode();
		try {
			/** 白名单试单 **/
			SCfgParamVO sCfgParamVO = new SCfgParamVO();
			sCfgParamVO.setParamKey(ConsoleEnums.ParamKey.BD_TRIAL_ORDER_SWITCH.getValue());
			sCfgParamVO.setParamValue(BaseConstant.YES_Y);
			sCfgParamVO = sCfgParamService.queryByCondition(sCfgParamVO);
			if(Objects.nonNull(sCfgParamVO)) {
				CusWhiteListTecVO cusWhiteListTecVO = new CusWhiteListTecVO();
				cusWhiteListTecVO.setCertType(certType);
				cusWhiteListTecVO.setCertNo(certCode);
				cusWhiteListTecVO = cusWhiteListTecService.queryByCertNoAndTyp(cusWhiteListTecVO);
				if(Objects.isNull(cusWhiteListTecVO)) {
					nlsCreditInfoVO.setBdReasonCode(MsLoanConstant.ReasonCodeMap.get("BdAccessRule010"));
					nlsCreditInfoVO.setBdReasonMsg(MsLoanConstant.ReasonMsgMap.get("BdAccessRule010"));
					nlsProcessBizVo.setRefuseMsg(RefuseType.OTHER.getMsg());
					throw new BizException("证件号码为： " + certCode + "的客户不存在白名单中，进件申请失败！");
				}
			}
			
			/**
			 * 百度新增需求——反洗钱名单校验
			 * @author qiuyf
			 * @date 2019/06/25
			 */
			NlsQueueSoltComnService nlsQueueSoltComnService = (NlsQueueSoltComnService) SpringContextUtils.getBean("nlsQueueSoltComnService");
			boolean isGreyBlackList = nlsQueueSoltComnService.isGreyBlackList(certType, certCode);
			if (isGreyBlackList) {
				nlsCreditInfoVO.setBdReasonCode(MsLoanConstant.ReasonCodeMap.get("BdAccessRule010"));
				nlsCreditInfoVO.setBdReasonMsg(MsLoanConstant.ReasonMsgMap.get("BdAccessRule010"));
				nlsProcessBizVo.setRefuseMsg(RefuseType.ISGREY_LIST.getMsg());
				throw new BizException("证件号码为： " + certCode + "命中反洗钱名单，进件申请失败！");
			}

			/* 名单系统校验 */
			// 百度不查关联人信息
			NLMHitQueryReports hitQueryReports = NLMHitQueryReports.builder()
					// 	客户中文名称
					.custCnnm(nlsCreditInfoVO.getCusName())
					// 	证件类型
					.identTp(nlsCreditInfoVO.getCertType())
					// 	证件号码
					.identNo(nlsCreditInfoVO.getCertCode())
					// 境内外标志：默认境内
					.dmstFrgnFlg("1")
					// 客户类型：默认个人
					.custTp("01")
					.build();

			NLMHitQueryResponseReports zx11008Content = null;
			boolean isConnected = true;
			try {
				zx11008Content = zx11008Service.service(hitQueryReports);
			} catch (Exception e) {
				log.error(e.getMessage());
				log.error("调用名单系统异常", e);
				isConnected = false;
			}
			if (isConnected){
				log.info("名单系统规则参数准备,流水号：" + applySeq + ",查询名单系统接口完成");
				Objects.requireNonNull(zx11008Content);
				// Y：命中黑名单，提示类不拒绝
				if ("Y".equals(zx11008Content.getBlklistCustFlg())) {
					log.info("命中名单类型信息：{}", zx11008Content.getHitInf());
					if ("PRMT".equals(zx11008Content.getHitInf())){
						// 不阻断流程
						log.warn("命中提示类黑名单，证件号码为{}",certCode);
					}else {
						String msg = String.format("命中黑名单,类型：%s,证件号码为：%s,提示信息：%s,超链接：%s",
								zx11008Content.getHitInf(), certCode, zx11008Content.getSugstMsg(), zx11008Content.getHyprlnksAddr());
						// refuse_cause 字段最大长度为500
						int maxLen = 500;
						if (StringUtils.isNotBlank(msg) && msg.length() > maxLen) {
							msg = msg.substring(0, maxLen - 4) + "...";
						}
						nlsCreditInfoVO.setBdReasonCode("MDXT_40130002_10");
						nlsCreditInfoVO.setBdReasonMsg("证件号码为： " + certCode + "命中开户交易名单，进件申请失败！");
						nlsProcessBizVo.setRefuseMsg("证件号码为： " + certCode + "命中开户交易名单，进件申请失败！");
						throw new BizException(msg);
					}
				}
			}



			/** 产品校验开始 **/
			log.info("开始进行准入规则产品校验,流水号:" + applySeq);
			Objects.requireNonNull(nlsCreditInfoVO);
			String prdId = Objects.requireNonNull(nlsCreditInfoVO.getPrdId());
			PrdInfoVO prdInfoVo = new PrdInfoVO();
			prdInfoVo.setPrdId(prdId);
			prdInfoVo = prdInfoService.queryByPk(prdInfoVo);

			PrdStatEnums prdStat = PrdStatEnums.parse(prdInfoVo.getPrdStatus());
			if (!Objects.equals(prdStat, PrdStatEnums.IN_USE)) {
				nlsProcessBizVo.setRefuseMsg(RefuseType.OTHER.getMsg());
				throw new BizException("产品： " + prdInfoVo.getPrdName() + " 不是有效状态");
			}
			//授信金额
			BigDecimal applyAmt = nlsCreditInfoVO.getApplyAmt();
			//最大授信金额
			BigDecimal maxAmt = prdInfoVo.getCrdAprvMaxAmt();
			//最小授信金额
			BigDecimal minAmt = prdInfoVo.getCrdAprvMinAmt();
			
			/** 产品校验结束 **/
			// 年龄限制
			int age = ValidationUtils.getAgeFromIdNumber(nlsCreditInfoVO.getCertCode(),BaseConstant.BD_AGE_START_23,BaseConstant.BD_AGE_END_55);
			// 人群分层判断
			int bdPreCustSeg = 0;
			String preCustSeg = nlsCreditInfoVO.getBdPreCustSeg();
			if(!Objects.equals(PRE_CUST_SEG_A, preCustSeg) && !Objects.equals(PRE_CUST_SEG_B, preCustSeg)) {
				bdPreCustSeg = 1;
			}
			// 行员核查
			int bankerHitsNum = 0;
			CusRelCheckVO cusRelCheckVO = cusRelCheckService.queryCusRelCheckBycertCode (MD5Util.getMD5ofStr(nlsCreditInfoVO.getCertCode()).toUpperCase(),nlsCreditInfoVO.getCertType());
			if (cusRelCheckVO != null && Objects.equals(RELATION_BANKER, cusRelCheckVO.getRelRelation()) && Objects.equals(cusRelCheckVO.getSts(), ON_WORK)) {
				bankerHitsNum = 1;
			}
			// 关联人核查
			int relationHitsNum = 0;
			cusRelCheckVO = cusRelCheckService.queryCusRelCheckBycertCode (nlsCreditInfoVO.getCertCode(), nlsCreditInfoVO.getCertType());
			if (cusRelCheckVO != null && Objects.equals(RELATION_ASSOCIATE, cusRelCheckVO.getRelRelation())) {
				relationHitsNum = 1;
			}
			/** 非百度产品余额判断开始 */
			// 该客户其他产品
			NlsApplyInfoVO nlsApplyInfoVO = new NlsApplyInfoVO();
			nlsApplyInfoVO.setCertCode(nlsCreditInfoVO.getCertCode());
			nlsApplyInfoVO.setPrdCode(nlsCreditInfoVO.getPrdCode());
//			AccLoanVO accLoanVO = new AccLoanVO();
//			accLoanVO.setCertType(nlsCreditInfoVO.getCertType());
//			accLoanVO.setCertCode(nlsCreditInfoVO.getCertCode());
//			LmtPrdContVO lmtPrdContVO = new LmtPrdContVO();
//			lmtPrdContVO.setCertType(nlsCreditInfoVO.getCertType());
//			lmtPrdContVO.setCertCode(nlsCreditInfoVO.getCertCode());
//			List<AccLoanVO> accLoanVOs = Optional.ofNullable(accLoanService.queryAccLoanListByCondition(accLoanVO)).orElseGet(Collections::emptyList);
//			BigDecimal totalAmt = accLoanVOs.stream().filter(vo -> {
//				String accStatus = vo.getAccountStatus();
//				return accStatus != null && !accStatus.equals(LoanConstant.TZZT_JQ) && !accStatus.equals(LoanConstant.TZZT_CX)
//						&& !accStatus.equals(LoanConstant.TZZT_CZ) && !accStatus.equals(LoanConstant.TZZT_LPJQ);
//			}).map(AccLoanVO::getLoanBalance).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
			// 根据审批状态分组
			Map<String, List<NlsApplyInfoVO>> groupMap = Optional.ofNullable(nlsApplyInfoService.queryCertCode(nlsApplyInfoVO))
					.orElseGet(Collections::emptyList).stream()
					.collect(Collectors.groupingBy(NlsApplyInfoVO::getNlsApplyState));
			log.info("获取审批中的--申请金额");
			BigDecimal totalAmt = BigDecimal.ZERO;
			// 获取审批中的--申请金额
			totalAmt = Optional.ofNullable(groupMap.get(MsLoanConstant.NlsApplyStateEnum.APPR.VALUE)).orElseGet(ArrayList::new)
					.stream().map(NlsApplyInfoVO::getApplyAmt).filter(Objects::nonNull)
					.reduce(totalAmt, BigDecimal::add);
			log.info("获取审批通过,待放款--申请金额");
			// 获取审批通过--申请金额
			totalAmt = Optional.ofNullable(groupMap.get(MsLoanConstant.NlsApplyStateEnum.PASS.VALUE)).orElseGet(ArrayList::new)
					.stream().filter(i -> i.getDnSts() != null && i.getDnSts().equals(BaseConstant.DN_STS_3))
					.map(NlsApplyInfoVO::getApplyAmt).filter(Objects::nonNull)
					.reduce(totalAmt, BigDecimal::add);
			/** 非百度产品余额判断结束 */
			
			BigDecimal totalAmt1 = BigDecimal.ZERO;
			/** 百度产品余额 */
			/*List<LmtPrdContVO> lmtPrdContVOs = Optional.ofNullable(lmtPrdContService.queryLmtPrdList(lmtPrdContVO)).orElseGet(Collections::emptyList);
			totalAmt1 = lmtPrdContVOs.stream().filter(vo -> {
				String lmtStatus = vo.getLmtStatus();
				return lmtStatus != null && lmtStatus.equals(ConsoleConstant.LIMIT_STS_02);
			}).map(LmtPrdContVO::getAvailAmt).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);*/
			// 根据审批状态分组
			NlsCreditInfoVO nlsCreditCode = new NlsCreditInfoVO();
			nlsCreditCode.setCertCode(nlsCreditInfoVO.getCertCode());
			nlsCreditCode.setPrdCode(nlsCreditInfoVO.getPrdCode());
			Map<String, List<NlsCreditInfoVO>> creditMap = Optional.ofNullable(nlsCreditInfoService.queryCertCode(nlsCreditCode))
					.orElseGet(Collections::emptyList).stream()
					.collect(Collectors.groupingBy(NlsCreditInfoVO::getApprvSts));
			log.info("获取审批中的--申请金额");
			// 获取审批中的--申请金额
			totalAmt1 = Optional.ofNullable(creditMap.get(MsLoanConstant.NlsApplyStateEnum.APPR.VALUE)).orElseGet(ArrayList::new)
					.stream().filter(i -> !applySeq.equals(i.getLmtApplySeq())).map(NlsCreditInfoVO::getApplyAmt).filter(Objects::nonNull)
					.reduce(totalAmt1, BigDecimal::add);
			log.info("获取审批通过--申请金额");
			// 获取审批通过--申请金额
//			totalAmt1 = Optional.ofNullable(creditMap.get(MsLoanConstant.NlsApplyStateEnum.PASS.VALUE)).orElseGet(ArrayList::new)
//					.stream().filter(i -> i.getApplyAmt() != null)
//					.map(NlsCreditInfoVO::getApplyAmt).filter(Objects::nonNull)
//					.reduce(totalAmt1, BigDecimal::add);
			/** 百度产品余额 */
			/** 初始化规则过程表 */
			NlsProcessRuleVO nlsProcessRuleVO = new NlsProcessRuleVO();
			nlsProcessRuleVO.setApplySeq(applySeq);
			nlsProcessRuleVO.setCreateTime(DateUtility.getCurrDateTime());
			nlsProcessRuleVO.setLastModifyTime(DateUtility.getCurrDateTime());
			nlsProcessRuleVO.setPrdCode(prdId);
			nlsProcessRuleVO.setPrdName(nlsCreditInfoVO.getPrdName());
			nlsProcessRuleVO.setCusName(nlsCreditInfoVO.getCusName());
			nlsProcessRuleVO.setCusId(nlsCreditInfoVO.getCusId());
			
			/** 执行规则 **/
			log.info("准入规则参数准备,流水号：" + applySeq + ",开始执行准入规则");
			ReqRuleVo reqRuleVo = new ReqRuleVo();
			reqRuleVo.setAppNo(applySeq);
			reqRuleVo.setPrdName(nlsCreditInfoVO.getPrdName());
			ReqAccessRuleVo param = new ReqAccessRuleVo();
			param.setBankerHitsNum(bankerHitsNum);
			param.setRelatedHitsNum(relationHitsNum);
			param.setAge(age);
			param.setLoanAmt(applyAmt);
			param.setMaxLoanAmt(maxAmt);
			param.setMinLoanAmt(minAmt);
			param.setCusLoanBal(totalAmt);
			param.setMaxLoanBal(prdInfoVo.getLoanMaxAmtBalance());
			param.setPreCustSeg(bdPreCustSeg);
			param.setBdCreditAmt(totalAmt1);
			param.setAvailApply(true);
			reqRuleVo.setReqAccessRuleVo(param);

			/**批量执行规则**/
			List<RespRuleVO> list = ruleService.executeBatchRule(Rule.RuleType.BDACCESS, reqRuleVo);
			/**规则结果分组**/
			Map<String, List<RespRuleVO>> map = list.stream().collect(Collectors.groupingBy(RespRuleVO::getRuleId));
			/** 规则结果信息  反馈给开放平台*/
			String refuseMsg = "";
			String reasonCode = "";
			/** 添加要执行的规则 */
			List<PrdSoltRuleVO> prdSoltRules = prdSoltRuleService.queryByPrdIdAndSoltCode(prdId, slotCode);
			/** 规则结果信息  记录拒绝原因细类*/
			StringBuilder sb = new StringBuilder();
			for (int j = 0; j < prdSoltRules.size(); j++) {
				String order = prdSoltRules.get(j).getRuleOrder();
				/** 规则名称method映射 */
				Method methodRuleName = NlsProcessRuleVO.class.getMethod("setRuleName" + order, String.class);
				/** 规则状态method映射 */
				Method methodRuleState = NlsProcessRuleVO.class.getMethod("setRuleState" + order, String.class);
				/** 规则时间method映射 */
				Method methodRuleTime = NlsProcessRuleVO.class.getMethod("setRuleTime" + order, String.class);

				methodRuleName.invoke(nlsProcessRuleVO, prdSoltRules.get(j).getRuleCode());
				methodRuleTime.invoke(nlsProcessRuleVO, DateUtility.getCurrAppDateTimeString());
				if (map.get(prdSoltRules.get(j).getRuleCode()) != null) {
					methodRuleState.invoke(nlsProcessRuleVO, PrdAdaptedSoltEnums.SoltProcessStateEnum.FAILURE.getCode());
					sb.append(prdSoltRules.get(j).getRuleName()).append("规则不通过;");
					/**根据规则id映射返回开放平台错误信息**/
					reasonCode = MsLoanConstant.ReasonCodeMap.get(prdSoltRules.get(j).getRuleCode());
					refuseMsg = MsLoanConstant.ReasonMsgMap.get(prdSoltRules.get(j).getRuleCode());
					break;
				} else {
					methodRuleState.invoke(nlsProcessRuleVO, PrdAdaptedSoltEnums.SoltProcessStateEnum.SUCCESS.getCode());
				}
			}
			/**设置百度原因说明**/
			nlsCreditInfoVO.setBdReasonCode(reasonCode);
			nlsCreditInfoVO.setBdReasonMsg(refuseMsg);
			/** 插入规则过程表 */
			nlsProcessRuleService.insertNlsProcessRule(nlsProcessRuleVO);

			// 命中规则异常处理
			if (!list.isEmpty()) {
				throw new BizException(sb.toString());
			}
		} catch (Exception e) {
			log.error(Arrays.toString(e.getStackTrace()));
			nlsProcessBizVo.setExceptionFlag(true);
			nlsProcessBizVo.setExceptionMsg(e);
		}
	}

	private NLMHitQueryReports.RltdPrsnInfArry rltdPrsnInfArryConverter(CusIndivRelVO cusIndivRelVO) {
		return NLMHitQueryReports.RltdPrsnInfArry.builder()
				// 	关系类型
				.relaTp(cusIndivRelVO.getRelation())
				// 	关联客户名称
				.lnkCustNm(cusIndivRelVO.getRelName())
				// 	关联人证件类型
				.rltvPrsnIdentTp(cusIndivRelVO.getCertType())
				// 	关联人证件号
				.rltvPrsnIdentNo(cusIndivRelVO.getCertCode())
				// 	关联人国籍
//					.rltvPrsnNation()
				.build();
	}

	@Autowired
	@Qualifier("nlsApplyInfoService")
	private NlsApplyInfoService nlsApplyInfoService;
	@Autowired
	@Qualifier("nlsCreditInfoService")
	private NlsCreditInfoService nlsCreditInfoService;
	@Autowired
	@Qualifier("prdInfoService")
	private PrdInfoService prdInfoService;
	@Autowired
	@Qualifier("ruleCallService")
	private RuleCallService ruleService;
	@Autowired
	@Qualifier("zx11001Service")
	private ZXService<ZX11001QueryReports> zx11001Service;
	@Autowired
	@Qualifier("ctrLoanContService")
	private CtrLoanContService ctrLoanContService;
	@Autowired
	@Qualifier("cusRelCheckService")
	private CusRelCheckService cusRelCheckService;
	@Autowired
	@Qualifier("nlsProcessRuleService")
	private NlsProcessRuleService nlsProcessRuleService;
	@Autowired
	@Qualifier("prdSoltRuleService")
	private PrdSoltRuleService prdSoltRuleService;
	@Autowired
	@Qualifier("nlsProcessBizService")
	private NlsProcessBizService nlsProcessBizService;
	@Autowired
	@Qualifier("accLoanService")
	private AccLoanService accLoanService;
	@Autowired
	@Qualifier("lmtPrdContService")
	private LmtPrdContService lmtPrdContService;
	@Autowired
	@Qualifier("sCfgParamService")
	private SCfgParamService sCfgParamService;
	@Autowired
	@Qualifier("cusWhiteListTecService")
	private CusWhiteListTecService cusWhiteListTecService;
	@Autowired
	@Qualifier("zx11008Service")
	private EsbService<NLMHitQueryReports,NLMHitQueryResponseReports> zx11008Service;

	private static final String RELATION_BANKER = "01"; // 关联关系，行员
	private static final String RELATION_ASSOCIATE = "02"; // 关联关系，关联人
	private static final String ON_WORK = "0"; // 关联人核查--在职
	private static final String PRE_CUST_SEG_A = "A"; // 人群分层A
	private static final String PRE_CUST_SEG_B = "B"; // 人群分层B
}